import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Date;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.regex.Pattern;

import net.spy.memcached.MemcachedClient;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;

import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.CSVWriter;

/*
 * This class handles connection to mongodb
 */
public class Dbase {

	private String serverName = "127.0.0.1";
	private int portNumber = 27017;
	private String fileName;
	private String DbName = "bus_routing";
	private String collectionName = "datatable";
	private String[] colName;
	private MongoClient mongoclient;
	private DB db;
	private DBCollection collection;
	private int numOfPoint;
	private int[] listOfPointIndex;
	private StopWatch stop;
	private final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd hh:mm:ss z yyyy");
	private double[][] direction;
	private Timestamp[] time;
	
	public Dbase (String fileName) {
		stop = new StopWatch();
		this.fileName = fileName;
		parseFromFile();
		connection();

		System.out.println("------------------------------------------------------------------------");
		listOfPointIndex = getListOfPoints();
		direction = getDirections();
		//time = getTime();
		System.out.println("------------------------------------------------------------------------");
		System.out.println("");
	}	
	
	/* Return Object by index and time */
	public DBCursor getObject(Timestamp time) {
		stop.start();
		String string = dateFormat.format(time);
		string = string.substring(0, string.length() - 2);
		Pattern p = Pattern.compile(string + "*");
		BasicDBObject object = new BasicDBObject(colName[6], p);	
		
		DBCursor cursor = collection.find(object);
		stop.stop();
		System.out.println("Time to get Cursor at Timestamp " 
								+ string.substring(0, string.length() - 1) + " is: " 
								+ stop.getElapsedTime() + " ms");
		System.out.println("");
		return cursor;
	}
	
	public double getMinLatitude() {
		return direction[0][0];
	}
	
	public double getMaxLatitude() {
		return direction[0][1];
	}
	
	public double getMinLongtitude() {
		return direction[1][0];
	}
	
	public double getMaxLongtitude() {
		return direction[1][1];
	}
	
	public Timestamp getSoonestTime() {
		return time[0];
	}
	
	public Timestamp getLatestTime() {
		return time[1];
	}
	
 	public int[] getListOfPointIndex() {
		return listOfPointIndex;
	}

	public String[] getColName() {
		return colName;
	}

	public int getNumOfPoint() {
		return numOfPoint;
	}
	
	private void parseFromFile () {
			CSVReader reader;
			try {
				reader = new CSVReader(new FileReader(fileName));
				colName = reader.readNext();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}		
	}
	
	private void connection() {
		try {
			System.out.println("------------------------------------------------------------------------");
			System.out.println("Connection to server ...");
			mongoclient = new MongoClient(serverName, portNumber);
			db = mongoclient.getDB(DbName);
			collection = db.getCollection(collectionName);
			System.out.println("Connect succeed!");
			System.out.println("------------------------------------------------------------------------");
			System.out.println("");
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public DBCursor test() {
		System.out.println("Start Test");
		BasicDBObject object = new BasicDBObject(colName[6], 1);
		
		DBCursor cursor = collection.find().sort(object);
		return cursor;
		/*int count = 0;
		stop.start();
		while (cursor.hasNext()) {
			System.out.println("");
			System.out.println("---------------------");
			DBObject temp = cursor.next();
			System.out.println(temp.get(colName[0]) + " " + temp.get(colName[1]) + " " + temp.get(colName[2]) + " " + temp.get(colName[6]));
			count++;
		}
		stop.stop();
		System.out.println("Time: " + stop.getElapsedTime() + " ms");
		System.out.println("Stop Test with " + count + " cases!");*/
	}
	
	public void exportFile(String fileName) {
		BasicDBObject object = new BasicDBObject(colName[6], 1);
		DBCursor cursor = collection.find().sort(object);
		try {
			DBCursor head = cursor;
			long startTime = dateFormat.parse(head.next().get(colName[6]).toString()).getTime() / 1000;
			// use FileWriter constructor that specifies open for appending
			CSVWriter csvOutput = new CSVWriter(new FileWriter(fileName, true), ',');
			csvOutput.writeNext(colName);
			
			while(cursor.hasNext()) {
				DBObject obj = cursor.next();
				String[] result = new String[7];
				for(int i = 0; i < 6; i++) {
					result[i] = obj.get(colName[i]).toString();
				}
				result[6] = (new Long(dateFormat.parse(obj.get(colName[6]).toString()).getTime() / 1000 - startTime)).toString();
				csvOutput.writeNext(result);
				csvOutput.flush();
			}
			csvOutput.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public void exportFilterFile(String fileName) {
		BasicDBObject object = new BasicDBObject(colName[6], 1);
		DBCursor cursor = collection.find().sort(object);
		try {
			System.out.println(dateFormat.parse(cursor.next().get(colName[6]).toString()));
			long startTime = dateFormat.parse(cursor.next().get(colName[6]).toString()).getTime() + 6 * 60 * 60 * 1000;
			long endTime = startTime + 3 * 24 * 60 * 60 * 1000 ;
			int numberOfRecords = 100000;
			exportFileWithTime(fileName, cursor, startTime, endTime, numberOfRecords);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	private void exportFileWithTime(String fileName, DBCursor cursor, long startTime, long endTime, int numberOfRecords) {
		try {
			int count = 0;
			CSVWriter csvOutput = new CSVWriter(new FileWriter(fileName, true), ',');
			csvOutput.writeNext(colName);
			
			while(cursor.hasNext()) {
				DBObject obj = cursor.next();
				if(dateFormat.parse(obj.get(colName[6]).toString()).getTime() > startTime) {
					String[] result = new String[7];
					for(int i = 0; i < 6; i++) {
						result[i] = obj.get(colName[i]).toString();
					}
					result[6] = (new Long((dateFormat.parse(obj.get(colName[6]).toString()).getTime() - startTime) / 1000)).toString();
					csvOutput.writeNext(result);
					csvOutput.flush();
					count++;
				} 
				if (dateFormat.parse(obj.get(colName[6]).toString()).getTime() > endTime || count > numberOfRecords) {
					break;
				}
			}
			csvOutput.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/* return min, max longtitude and latitude */
	private double[][] getDirections() {
		double[][] temp = new double[2][2];
		BasicDBObject minLatitude = new BasicDBObject(colName[1], 1);
		stop.start();
		DBCursor cursor = collection.find().sort(minLatitude).limit(1);
		temp[0][0] = (Double) cursor.next().get(colName[1]);
		stop.stop();
		System.out.println("Time to get Min Latitude: " + stop.getElapsedTime() + " ms");
		
		BasicDBObject maxLatitude = new BasicDBObject(colName[1], -1);
		stop.start();
		cursor = collection.find().sort(maxLatitude).limit(1);
		temp[0][1] = (Double) cursor.next().get(colName[1]);
		stop.stop();
		System.out.println("Time to get Max Latitude: " + stop.getElapsedTime() + " ms");
		
		BasicDBObject minLongtitude = new BasicDBObject(colName[2], 1);
		stop.start();
		cursor = collection.find().sort(minLongtitude).limit(1);
		temp[1][0] = (Double) cursor.next().get(colName[2]);
		stop.stop();
		System.out.println("Time to get Min Longtitude: " + stop.getElapsedTime() + " ms");
		
		BasicDBObject maxLongtitude = new BasicDBObject(colName[2], -1);
		stop.start();
		cursor = collection.find().sort(maxLongtitude).limit(1);
		temp[1][1] = (Double) cursor.next().get(colName[2]);
		stop.stop();
		System.out.println("Time to get Max Longtitude: " + stop.getElapsedTime() + " ms");		
		
		return temp;
	}
	
	/* Return soonest timestamp and latest timestamp */
	public Timestamp[] getTime() {
		Timestamp[] timestamp = new Timestamp[2];
		Date parsedDate;
		BasicDBObject soonestTime, latestTime;
		
		try {
			soonestTime = new BasicDBObject(colName[6], 1);
			stop.start();
			DBCursor cursor = collection.find().sort(soonestTime).limit(1);
			parsedDate = dateFormat.parse(cursor.next().get(colName[6]).toString());
			timestamp[0] = new Timestamp(parsedDate.getTime()); 
			stop.stop();
			System.out.println("Time to get Soonest Time: " + stop.getElapsedTime() + " ms");
			
			latestTime = new BasicDBObject(colName[6], -1);
			stop.start();
			cursor = collection.find().sort(latestTime).limit(1);
			parsedDate = dateFormat.parse(cursor.next().get(colName[6]).toString());
			timestamp[1] = new Timestamp(parsedDate.getTime());
			stop.stop();
			System.out.println("Time to get Latest Time: " + stop.getElapsedTime() + " ms");
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		return timestamp;
		
	}
	
	/* Return list of point */
	private int[] getListOfPoints() {
		stop.start();
		List<Integer> list = (List<Integer>) collection.distinct(colName[0]);
		numOfPoint = list.size();
		int[] returnList = makeIntArray(list);
		stop.stop();
		System.out.println("Time to get List of Points: " + stop.getElapsedTime() + " ms");
		System.out.println("Number of Points: " + numOfPoint);
		return returnList;
	}
	
	private int[] makeIntArray(List<Integer> list) {
		int[] returnList = new int[list.size()];
		for(int i = 0; i < list.size(); i++) {
			returnList[i] = list.get(i);
		}
		return returnList;
	}
	
	
	
}
